// Ŭnicode please

#include "SRModelLoader.h"
#include "SRObjModelLoader.h"
#include "SRPrimitiveModel.h"
#include "SRMacro.h"
#include <sstream>

using std::string;
using std::map;

namespace sora
{
	ModelLoader &ModelLoader::loader()
	{
		static ModelLoader ctx;
		return ctx;
	}
	ModelPtr ModelLoader::load(const std::string &path)
	{
		//이미 등록되어있는지 확인
		if(ModelPool::pool().isExist(path) == true)
			return ModelPool::pool().get(path);

		//else...등록되어있지 않으면 새로 생성 + 등록
		string ext = mio::getPath().extension(path);
		if(ext == "obj")
		{
			Model *model = objLoad(path);
			ModelPool::pool().add(path, model);
			return ModelPool::pool().get(path);
		}
		//else..
		SR_ASSERT(false && "not support yet");
		//return ModelPtr(NULL);
	}
	ModelPtr ModelLoader::loadBoundingBox(ModelPtr model)
	{
		//이름 생성하기(포인터로 하면 아마도 중복이 없겠지?
		Model* modelPtr = model.get();
		std::ostringstream oss;
		oss << modelPtr << "_boundingbox";
		const std::string key = oss.str();

		if(ModelPool::pool().isExist(key))
		{
			return ModelPool::pool().get(key);
		}

		//else...
		BoundingBox *box = new BoundingBox(model.get());

		//풀에 넣기
		ModelPool::pool().add(key, box);
		return ModelPool::pool().get(key);

	}
	const std::string ModelLoader::color2string(const matsu::vec4 &color) const
	{
		std::ostringstream oss;
		oss << "(" << color.x << "," << color.y << "," << color.z << "," << color.w << ")";
		return oss.str();
	}
	ModelPtr ModelLoader::loadWireCube(float width, float height, float depth, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "wirecube_" << width << "_" << height << "_" << depth << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireCube(width, height, depth, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadSolidCube(float width, float height, float depth, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "solidcube_" << width << "_" << height << "_" << depth << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new SolidCube(width, height, depth, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadWireSphere(float radius, int slices, int stacks, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "wiresphere_" << radius << "_" << slices << "_" << stacks << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireSphere(radius, slices, stacks, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadSolidSphere(float radius, int slices, int stacks, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "solidsphere_" << radius << "_" << slices << "_" << stacks << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new SolidSphere(radius, slices, stacks, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadWireCone(float base, float height, int slices, int stacks, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "wirecone_" << base << "_" << height << "_" << slices << "_" << stacks << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireCone(base, height, slices, stacks, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadSolidSphere(float base, float height, int slices, int stacks, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "solidcone_" << base << "_" << height << "_" << slices << "_" << stacks << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new SolidCone(base, height, slices, stacks, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadWireTeapot(float size, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "wireteapot_" << size << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireTeapot(size, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadSolidTeapot(float size, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "solidteapot_" << size << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new SolidTeapot(size, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadWireCylinder(float radius, float height, int slices, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "wirecylinder_" << radius << "_" << height << "_" << slices << "_" << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireCylinder(radius, height, slices, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadSolidCylinder(float radius, float height, int slices, const matsu::vec4 &color)
	{
		std::ostringstream oss;
		oss << "solidcylinder_" << radius << "_" << height << "_" << slices << "_" << "_" << color2string(color);
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new SolidCylinder(radius, height, slices, color);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}
	ModelPtr ModelLoader::loadWireAxis(float size)
	{
		std::ostringstream oss;
		oss << "wireaxis_" << size;
		string key = oss.str();

		if(ModelPool::pool().isExist(key))
			return ModelPool::pool().get(key);
		else
		{
			Model *model = new WireAxis(size);
			ModelPool::pool().add(key, model);
			return ModelPool::pool().get(key);
		}
	}

	/*
	Model *ModelLoader::loadWireframe(Model *mesh)
	{
		string key = ModelPool::pool().getKey(mesh);
		key += "_wireframe";

		//이미 등록된 모델인 경우
		if(ModelPool::pool().isExist(key) == true)
			return ModelPool::pool().get(key);

		if(mesh->type() == ModelTypeMesh)
		{
			MeshModel *m = static_cast<MeshModel*> (mesh);
			Model *wireframe = new WireFrameModel(*m);
			//wireframe등록
			ModelPool::pool().add(key, wireframe);
			return wireframe;
		}
		SR_ASSERT(!"not valid model");
		return NULL;
	}
	*/

	Model *ModelLoader::objLoad(const std::string &path)
	{
		static ObjModelLoader loader;
		ModelPool &pool = ModelPool::pool();
		Model *model = loader.load(path);
		return model;
	}

	ModelPool &ModelPool::pool()
	{
		static ModelPool p;
		return p;
	}
	ModelPool::~ModelPool()
	{
	}
	bool ModelPool::isExist(const std::string &key) const
	{
		map<string, ModelPtr>::const_iterator iter = data_.find(key);
		if(iter == data_.end())
			return false;
		else
			return true;
	}
	bool ModelPool::isExist(const char *key) const
	{
		const string keyStr(key);
		return isExist(keyStr);
	}
	void ModelPool::add(const std::string &key, Model *model)
	{
		//실팽중 모델 파일 변경은 불가능하니까 똑같은건데 내용이 바뀔일은 없다
		SR_ASSERT(isExist(key) == false);
		ModelPtr m(model);
		data_[key] = m;
	}
	void ModelPool::add(const char *key, Model *model)
	{
		const string keyStr(key);
		add(keyStr, model);
	}
	ModelPtr ModelPool::get(const std::string &key) const
	{
		map<string, ModelPtr>::const_iterator iter = data_.find(key);
		SR_ASSERT(iter != data_.end());
		return iter->second;
	}
	ModelPtr ModelPool::get(const char *key) const
	{
		const string keyStr(key);
		return get(keyStr);
	}

	const std::string ModelPool::getKey(Model *model) const
	{
		map<string, ModelPtr>::const_iterator iter;
		for(iter = data_.begin() ; iter != data_.end() ; iter++)
		{
			Model* m = iter->second.get();
			if(model == m)
				return iter->first;
		}
		SR_ASSERT(false && "same key model is not exist");
		return NULL;
	}

	void ModelPool::removeUnusedModel()
	{
		//TODO:
		/*
		map<string, ModelPtr>::const_iterator iter;
		for(iter = data_.begin() ; iter != data_.end() ; iter++)
		{
			if(iter->second.use_count() == 1)
			{
				data_.erase(iter);
			}
		}
		*/
	}
}
